import pandas as pd
import numpy as np
import csv
import math
import random
import matplotlib.pyplot as plt
from sklearn.metrics.pairwise import cosine_similarity
import torch
import torch.nn as nn
import time
import scipy.sparse as sps
from numpy.linalg import inv
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
class graphSingalMovie:
    def __init__(self,filePath,U,M,N,LENGTH,TEST):
        #filePath = "../data/drug_rat.csv"
        self.data = pd.read_csv(filePath)
        self.data = torch.tensor(self.data.values)
        self.U = U  # number of user用户数量
        self.M = M  # number of item电影数量
        self.COMMON_min = 2  # minimum number of common ratings used in constructing the network在网络建设中使用的最小公用评级数
        self.N = N  # k
        self.LENGTH = LENGTH#数据集大小
        self.TEST = TEST#测试集的大小
        self.b = torch.zeros(TEST, 1)  # the true ratings, after normalization归一化之后的评分
        self.F = torch.zeros(TEST, 9)  # number of ratings x degree of filters examined. 分阶之后的评分
        self.MU = torch.zeros(TEST, 1)
    def getSims(self,n):
        torch.manual_seed(1)
        perm_idx = torch.randperm(self.LENGTH)
        train_ALL_idx = torch.cat([perm_idx[0:self.TEST * n],perm_idx[self.TEST*(n+1):self.LENGTH]])  # training set
        r_train = torch.sparse.FloatTensor(
            torch.LongTensor(list([int(self.data[i, 0]), int(self.data[i, 1])] for i in train_ALL_idx)).t(),
            torch.FloatTensor(list(int(self.data[i, 2]) for i in train_ALL_idx)),
            torch.Size([self.U, self.M])).to_dense()
        r_train[r_train == 0] = float("NaN")
        self.mu_u = np.nanmean(r_train, axis=1)
        print("len(mu_m):{}".format(len(self.mu_u)))
        self.test_ALL_idx = perm_idx[self.TEST * n:self.TEST * (n+1)]
        self.r_ALL_meanC_train = r_train - np.tile(self.mu_u,self.M).reshape(self.M,self.U).T
        self.r_ALL_meanC_train = torch.tensor(np.array(pd.DataFrame(np.array(self.r_ALL_meanC_train)).fillna(0).values))
        self.isRated = r_train.clone()
        self.isRated[self.isRated > 0] = 1
        self.isRated = torch.tensor(np.array(pd.DataFrame(np.array(self.isRated)).fillna(0).values))
        self.sims = torch.tensor(np.array(pd.DataFrame(r_train.T).astype('float32').corr(method='pearson').fillna(0)))
        temp = torch.mm(self.isRated, self.isRated.T)
        U_min_check = temp.clone()
        U_min_check[U_min_check < self.COMMON_min] = 1
        U_min_check[U_min_check >= self.COMMON_min] = 0
        U_min_check = U_min_check - torch.diag_embed(torch.diag(U_min_check))
        self.sims[U_min_check == 1] = 0
    def getF(self):
        for i in range(len(self.test_ALL_idx)):
            u = self.data[self.test_ALL_idx[i], 0]
            m = self.data[self.test_ALL_idx[i], 1]
            A_crt = self.sims - torch.eye(self.U)
            A_crt[:, self.isRated[:, m] == 0] = 0  # 可能存在问题，待进一步验证
            temp = torch.sort(A_crt, dim=1, descending=True)[0]
            A_crt[A_crt < torch.tensor(np.tile(temp[:, self.N], self.U).reshape(self.U, self.U).T)] = 0
            A_crt = A_crt / torch.tensor(np.tile(sum(A_crt.T), self.U).reshape(self.U, self.U).T)
            A_crt = torch.tensor(np.array(pd.DataFrame(np.array(A_crt)).fillna(0).values))
            #print(A_crt[:,151])
            r_temp = self.r_ALL_meanC_train[:, m]
            self.b[i] = self.data[self.test_ALL_idx[i], 2] - torch.tensor(self.mu_u[u])
            self.MU[i] = torch.tensor(self.mu_u[u])
            temp = A_crt[u, :]
            self.F[i, 0] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 1] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 2] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 3] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 4] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 5] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 6] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 7] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))
            temp = torch.mm(temp.float().reshape(1, self.U), A_crt.float())
            self.F[i, 8] = torch.mm(temp.float().reshape(1, self.U), r_temp.float().reshape(self.U, 1))


            if i % 100 == 0 or i == len(self.test_ALL_idx) - 1:
                print("进度：{}%".format(round(i / (len(self.test_ALL_idx) - 1)* 100, 2)))
    def total_RMSE_new(self,a, F_crt, b, MU):
        b_hat = torch.mm(F_crt, a) + MU
        # print(b_hat)
        b_hat[b_hat > 5] = 5
        b_hat[b_hat < 1] = 1
        b_hat = b_hat - MU
        RMSE = 0
        for i in range(len(b)):
            RMSE += (b_hat[i] - b[i]) ** 2
        RMSE = RMSE / len(b)
        return float(RMSE[0]) ** 0.5
    def MAE(self,a, F_crt, b, MU):
        b_hat = torch.mm(F_crt, a) + MU
        # print(b_hat)
        b_hat[b_hat > 5] = 5
        b_hat[b_hat < 1] = 1
        b_hat = b_hat - MU
        mae = 0
        for i in range(len(b)):
            mae += math.fabs(b_hat[i] - b[i])
        mae = mae / len(b)
        return mae
    def getRMSE(self):
        rl = []
        ml = []
        for range_u in range(1, 10):
            C = 0.5
            F_crt = self.F[:, 0:range_u]
            a = torch.mm(torch.mm(torch.tensor(inv(torch.mm(F_crt.T, F_crt) + C * torch.eye(range_u))), F_crt.T), self.b)
            # print(torch.mm(F_crt.T,F_crt))
            # print(C * torch.mm(torch.mm(torch.eye(range_u),F_crt.T ),b))
            # print(a)
            r = self.total_RMSE_new(torch.tensor(a), F_crt, self.b, self.MU)
            rl.append(r)
            ma = self.MAE(torch.tensor(a), F_crt, self.b, self.MU)
            ml.append(ma)
            print("{}阶时，在C={}的情况下，RMSE为：{}".format(range_u, C,
                                                   self.total_RMSE_new(torch.tensor(a), F_crt, self.b, self.MU)))
            print("{}阶时，在C={}的情况下，MAE为：{}".format(range_u, C,
                                                   self.MAE(torch.tensor(a), F_crt, self.b, self.MU)))
        return [torch.tensor(rl),torch.tensor(ml)]
if __name__ == '__main__':
    movie = graphSingalMovie("./data/drug_rat.csv",759,994,40,37441,3744)
    RMSE = torch.zeros(1,9)
    mae = torch.zeros(1, 9)
    for i in range(10):
        movie.getSims(i)
        movie.getF()
        RMSE += movie.getRMSE()[0]
        mae += movie.getRMSE()[1]
        print("{}/10平均RMSE为：\n{}".format(i+1,RMSE/(i+1)))
        print("{}/10平均MAE为：\n{}".format(i + 1, mae / (i + 1)))